package com.mxx.common.data.utils;

import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * @Author itzgyw
 * @Date 2020-01-08
 * @Time 19:20
 * @Description:
 */
public class EntityReflectInvoke {
    private volatile static Map<Class, Map<String, Field>> fieldCacheMap;
    private volatile static Map<Class, String[]> sourceFieldNameCacheMap;


    static {
        fieldCacheMap = new HashMap<>();
        sourceFieldNameCacheMap = new HashMap<>();
    }

    /**
     * 实例化一个实体类
     *
     * @param classes
     * @param <T>
     * @return
     */
    public static <T> T newInterface(Class<T> classes) {
        try {
            return classes.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("No public parameterless constructor");
        }
    }

    /**
     * 获取targetClass 中的 sourceClass 也具有的字段名称
     *
     * @param sourceClass
     * @param targetClass
     * @return
     */
    public static String[] getSourceFieldFromTarget(Class sourceClass, Class targetClass) {
        String[] fieldNameArray = sourceFieldNameCacheMap.get(sourceClass);
        if (fieldNameArray == null) {
            synchronized (sourceClass) {
                fieldNameArray = sourceFieldNameCacheMap.get(sourceClass);
                if (fieldNameArray != null) {
                    return fieldNameArray;
                }
                Set<String> sourceField = getFieldName(sourceClass);
                Set<String> targetField = getFieldName(targetClass);
                Iterator<String> iterator = sourceField.iterator();
                List<String> existsField = new ArrayList<>();
                while (iterator.hasNext()) {
                    String fieldName = iterator.next();
                    if (targetField.contains(fieldName)) {
                        existsField.add(fieldName);
                    }
                }
                fieldNameArray = existsField.toArray(new String[0]);
                sourceFieldNameCacheMap.put(sourceClass, fieldNameArray);
            }
        }

        return fieldNameArray;
    }

    /**
     * 获取指定类的所有字段
     *
     * @param classes
     * @return
     */

    public static Set<String> getFieldName(Class classes) {
        Map<String, Field> map = getFieldMappingForCache(classes);
        return new HashSet<>(map.keySet());
    }

    /**
     * 设置指定字段的值
     *
     * @param entity
     * @param column
     * @param data
     */

    public static void set(Object entity, String column, Object data) {
        Field invokeField = getFieldNullThrowException(entity.getClass(), column);
        try {
            if (!invokeField.isAccessible()) {
                invokeField.setAccessible(true);
            }
            invokeField.set(entity, data);
        } catch (Exception e) {
            throw new IllegalArgumentException("Parameter type does not match", e);
        }
    }

    /**
     * 设置指定字段的值
     *
     * @param entity
     * @param columns
     * @param data
     */
    public static void set(Object entity, String[] columns, Object[] data) {

        Map<String, Field> map = getFieldMappingForCache(entity.getClass());
        try {
            for (int i = 0; i < columns.length; i++) {
                Field invokeField = map.get(columns[i]);
                if (invokeField == null) {
                    throw new IllegalArgumentException("not found field name " + columns[i] + " for " + entity.getClass().getName());
                }
                if (!invokeField.isAccessible()) {
                    invokeField.setAccessible(true);
                }
                invokeField.set(entity, data[i]);
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("Parameter type does not match", e);
        }
    }

    /**
     * 获取指定字段的值
     *
     * @param entity
     * @param column
     * @return
     */
    public static Object get(Object entity, String column) {
        Field invokeField = getFieldNullThrowException(entity.getClass(), column);
        try {
            if (!invokeField.isAccessible()) {
                invokeField.setAccessible(true);
            }
            return invokeField.get(entity);
        } catch (Exception e) {
            throw new IllegalArgumentException("Parameter type does not match", e);
        }
    }

    /**
     * 获取指定字段的值
     *
     * @param entity
     * @param columns
     * @return
     */
    public static Object[] get(Object entity, String[] columns) {
        Map<String, Field> map = getFieldMappingForCache(entity.getClass());
        try {
            Object[] data = new Object[columns.length];
            for (int i = 0; i < columns.length; i++) {
                Field invokeField = map.get(columns[i]);
                if (invokeField == null) {
                    throw new IllegalArgumentException("not found field name " + columns[i] + " for " + entity.getClass().getName());
                }
                if (!invokeField.isAccessible()) {
                    invokeField.setAccessible(true);
                }
                data[i] = invokeField.get(entity);
            }
            return data;
        } catch (Exception e) {
            throw new IllegalArgumentException("Parameter type does not match", e);
        }
    }

    /**
     * 获取字段，不存在则抛出异常
     *
     * @param classes
     * @param column
     * @return
     */
    public static Field getFieldNullThrowException(Class classes, String column) {
        Field invokeField = getFieldForCache(classes, column);
        if (invokeField == null) {
            throw new IllegalArgumentException("not found field name " + column + " for " + classes.getName());
        } else {
            return invokeField;
        }
    }


    /**
     * 在缓存中获取字段
     *
     * @param classes
     * @param column
     * @return
     */

    public static Field getFieldForCache(Class classes, String column) {
        Field field = getFieldMappingForCache(classes).get(column);
        return field;
    }

    /**
     * 在缓存中获取字段映射
     *
     * @param classes
     * @return
     */

    public static Map<String, Field> getFieldMappingForCache(Class classes) {
        Map<String, Field> fieldMap = fieldCacheMap.get(classes);
        if (fieldMap == null) {
            synchronized (classes) {
                fieldMap = fieldCacheMap.get(classes);
                if (fieldMap == null) {
                    fieldMap = new HashMap<>();
                    fieldCacheMap.put(classes, fieldMap);
                    eachClass(classes, classes);
                }
            }
        }
        return fieldMap;
    }

    private static void eachClass(Class sourceClass, Class targetClasses) {
        Field[] fields = targetClasses.getDeclaredFields();
        Map<String, Field> fieldMap = fieldCacheMap.get(sourceClass);
        for (Field field : fields) {
            if (field.getAnnotation(Transient.class) != null) {
                continue;
            }
            int modifier = field.getModifiers();
            if (Modifier.isFinal(modifier) || Modifier.isStatic(modifier)) {
                continue;
            }
            String fieldName = field.getName();
            fieldMap.put(fieldName, field);
        }
        Class superClass = targetClasses.getSuperclass();
        if (superClass.getAnnotation(MappedSuperclass.class) != null) {
            eachClass(sourceClass, superClass);
        }
    }
}
